package vitaliy.bondar.diamondcave;

import java.util.Random;
import java.util.Vector;

import org.andengine.engine.camera.Camera;
import org.andengine.entity.sprite.AnimatedSprite;
import org.andengine.opengl.vbo.VertexBufferObjectManager;

import android.graphics.Point;

public abstract class Enemy extends MovingGameObject implements LiveGameObject 
{
	protected Direction direction;
	private Random randomizer;
	Vector<Direction> ways;
	Vector<Direction> possibleWays;
    public Enemy( GameScene scene, final int pX, final int pY, VertexBufferObjectManager vbom, Camera camera )
    {
        super( scene, GameObject.Type.ENEMY, pX, pY, ResourcesManager.getInstance().enemy_region, vbom);
        randomizer = new Random();
        ways = new Vector<Direction>(4);
        possibleWays = new Vector<Direction>(4);
        ways.add(Direction.UP);
        ways.add(Direction.DOWN);
        ways.add(Direction.LEFT);
        ways.add(Direction.RIGHT);
        direction = ways.elementAt(0);
        this.animate(50, true );
    }
    
    @Override
    public boolean tryMove( Direction direction ) 
    {
    	Point next = this.nextCell(direction);
    	GameObject obstacle = m_scene.findItemAtPosition( next.x, next.y );
    	if(obstacle != null)
    	{
    		if(obstacle instanceof Player)
    		{
    			this.onPlayerContact();
    		}
    	}
    	return (obstacle == null);
    }
    
    public void randomMove() 
    {
    	possibleWays.clear();
    	Direction opposite = this.oppositeWay();
    	for (Direction way : ways) 
    	{
    		if(way != opposite && tryMove(way)) possibleWays.add(way);
		}
    	
    	if(possibleWays.isEmpty()) 
    	{
    		if(tryMove(opposite))
    		{
    			direction = opposite;
    		}
    		else
    		{
    			return;
    		}
    	}
    	else
    	{
    		int rand = randomizer.nextInt(possibleWays.size());
    		direction = possibleWays.elementAt(rand);
    	}
    	
		this.move(direction);
	}
    
    protected Point findWay(int ax, int ay, int bx, int by)
    {
        if (bx > ax)
        {
            if (by == ay && (bx - 1) == ax ) return new Point(bx, by);
            
            GameObject object = m_scene.findItemAtPosition(bx - 1, by);
            if (object == null)
            {
                Point p = this.findWay(ax, ay, bx - 1, by);
                if (p != null) return p;
            }
        }
        else if(bx < ax)
        {
            if (by == ay && (bx + 1) == ax ) return new Point(bx, by);
            
            GameObject object = m_scene.findItemAtPosition(bx + 1, by);
            if (object == null)
            {
                Point p = this.findWay(ax, ay, bx + 1, by);
                if (p != null) return p;
            }
        }
        if (by > ay)
        {
            if (bx == ax && (by - 1) == ay ) return new Point(bx, by);
            
            GameObject object = m_scene.findItemAtPosition(bx, by - 1);
            if (object == null)
            {
                Point p = this.findWay(ax, ay, bx, by - 1);
                if (p != null) return p;
            }
        }
        else if(by < ay)
        {
            if (bx == ax && (by + 1) == ay ) return new Point(bx, by);
            
            GameObject object = m_scene.findItemAtPosition(bx, by + 1);
            if (object == null)
            {
                Point p = this.findWay(ax, ay, bx, by + 1);
                if (p != null) return p;
            }
        }
        return null;
    }
    
    protected Direction oppositeWay()
    {
    	switch (direction) {
			case UP: return Direction.DOWN;
			case DOWN: return Direction.UP;
			case LEFT: return Direction.RIGHT;
			case RIGHT: return Direction.LEFT;
			default: return Direction.UP;
    	}
    }
    
    protected void onManagedUpdate(float pSecondsElapsed)
    {
        super.onManagedUpdate(pSecondsElapsed);
        if (this.m_isMoving) return;
        GameObject obstacle = m_scene.findItemAtPosition(cellX(), cellY() - 1);
        if (obstacle != null && obstacle instanceof FallingGameObject
                && ((FallingGameObject)obstacle).isKilling())
        {
            this.onDie();
        }
        Point next = this.findWay(this.cellX(), this.cellY(), m_scene.playerX(), m_scene.playerY());
        if (next == null)
        {
            this.randomMove();
        }
        else
        {
            if (m_scene.findItemAtPosition(next.x, next.y) instanceof Player) this.onPlayerContact();
            if (next.x > this.cellX())
                this.direction = Direction.RIGHT;
            else if (next.x < this.cellX())
                this.direction = Direction.LEFT;
            else if (next.y > this.cellY())
                this.direction = Direction.DOWN;
            else if (next.y < this.cellY()) this.direction = Direction.UP;
            this.move(next.x, next.y);
        }
    }
    
    @Override
    public void onDie()
    {
        ResourcesManager.getInstance().enemy_death.play();
        
        int fromX = Math.max(1, cellX() - 1);
        int toX = Math.min(m_scene.sceneSizeX() - 2, cellX() + 1);
        
        int fromY = Math.max(1, cellY() - 1);
        int toY = Math.min(m_scene.sceneSizeY() - 2, cellY() + 1);
        
        for (int i = fromY; i <= toY; ++i)
        {
            for (int j = fromX; j <= toX; ++j)
            {
                GameObject object = m_scene.findItemAtPosition(j, i);
                if (object != null)
                {
                    if (object.type() == Type.PLAYER) this.onPlayerContact();
                    object.setVisible(false);
                    object.setIgnoreUpdate(true);
                }
                
                final AnimatedCellEntity cell = new AnimatedCellEntity(j, i, CELL_WIDTH,
                        CELL_HEIGHT, ResourcesManager.getInstance().explosion_region, 
                        this.getVertexBufferObjectManager()){};
                m_scene.attachChild(cell);

                if (object == null || object.type() != Type.JEWEL)
                {
                    object = m_scene.makeJewel(j, i);
                    object.setCullingEnabled(true);
                    m_scene.setItemInPosition(j, i, object);
                    m_scene.attachChild(object);
                    object.setVisible(false);
                    object.setIgnoreUpdate(true);
                }
                
                final GameObject jewel = object;
                
                cell.animate(30, false, new IAnimationListener()
                {
                    @Override
                    public void onAnimationStarted(AnimatedSprite arg0, int arg1){}
                    @Override
                    public void onAnimationLoopFinished(AnimatedSprite a, int b, int c){}
                    @Override
                    public void onAnimationFrameChanged(AnimatedSprite a, int b, int c){}

                    @Override
                    public void onAnimationFinished(AnimatedSprite arg0)
                    {
                        cell.setVisible(false);
                        cell.setIgnoreUpdate(true);
                        jewel.setVisible(true);
                        jewel.setIgnoreUpdate(false);
                    }
                });
            }
        }
    }
}
